#include <sdk.h>
#include <wx/intl.h>
#include <wx/filename.h>
#include <wx/msgdlg.h>
#include <wx/stream.h>
#include <wx/wfstream.h>
#include <wx/txtstrm.h>
#include <wx/regex.h>
#include <compiler.h>
#include <M_Code_project.h>
#include <projectbuildtarget.h>
#include <globals.h>
#include <manager.h>
#include <logmanager.h>
#include <configmanager.h>
#include <macrosmanager.h>
#include "directcommands.h"
#include "compilercommandgenerator.h"
#include "compilergcc.h"
#include "filefilters.h"
#include <depslib.h>

const wxString COMPILER_SIMPLE_LOG( _T( "SLOG:" ) );
const wxString COMPILER_NOTE_LOG( _T( "SLOG:NLOG:" ) );
const wxString COMPILER_WARNING_LOG( _T( "SLOG:WLOG:" ) );
const wxString COMPILER_ERROR_LOG( _T( "SLOG:ELOG:" ) );
const wxString COMPILER_TARGET_CHANGE( _T( "TGT:" ) );
const wxString COMPILER_WAIT( _T( "WAIT" ) );
const wxString COMPILER_WAIT_LINK( _T( "LINK" ) );

const wxString COMPILER_NOTE_ID_LOG = COMPILER_NOTE_LOG.AfterFirst( _T( ':' ) );
const wxString COMPILER_WARNING_ID_LOG = COMPILER_WARNING_LOG.AfterFirst( _T( ':' ) );
const wxString COMPILER_ERROR_ID_LOG = COMPILER_ERROR_LOG.AfterFirst( _T( ':' ) );

DirectCommands::DirectCommands( CompilerGCC* compilerPlugin, Compiler* compiler, M_Code_Project* project, int logPageIndex ) :
  m_doYield( false ),
  m_PageIndex( logPageIndex ),
  m_pCompilerPlugin( compilerPlugin ),
  m_pCompiler( compiler ),
  m_pProject( project ),
  m_pGenerator( 0 ) {
  m_pGenerator = m_pCompiler->GetCommandGenerator( m_pProject );
  if( !m_pProject ) {
    return;
  }
  depsStart();
  wxFileName cwd;
  cwd.Assign( m_pProject->GetBasePath() );
  wxString depsCWD = cwd.GetPath( wxPATH_GET_VOLUME );
  Manager::Get()->GetLogManager()->DebugLog( F( _T( "CWD for depslib was: %s." ), depsCWD.wx_str() ) );
  if( ( depsCWD.Len() == 3 ) && ( depsCWD.GetChar( 1 ) == ':' ) && ( ( depsCWD.GetChar( 2 ) == '\\' ) || ( depsCWD.GetChar( 2 ) == '/' ) ) ) {
    depsCWD.RemoveLast();
  }
  Manager::Get()->GetLogManager()->DebugLog( F( _T( "CWD for depslib is: %s." ), depsCWD.wx_str() ) );
  depsSetCWD( depsCWD.mb_str() );
  wxFileName fname( m_pProject->GetFilename() );
  fname.SetExt( _T( "depend" ) );
  depsCacheRead( fname.GetFullPath().mb_str() );
}

DirectCommands::~DirectCommands() {
  if( !m_pProject ) {
    return;
  }
  struct depsStats stats;
  depsGetStats( &stats );
  if( stats.cache_updated ) {
    wxFileName fname( m_pProject->GetFilename() );
    fname.SetExt( _T( "depend" ) );
    depsCacheWrite( fname.GetFullPath().mb_str() );
  }
  Manager::Get()->GetLogManager()->DebugLog(
    F( _T( "Scanned %ld files for #includes, cache used %ld, cache updated %ld" ),
       stats.scanned, stats.cache_used, stats.cache_updated ) );
  depsDone();
  delete m_pGenerator;
}

void DirectCommands::AddCommandsToArray( const wxString& cmds, wxArrayString& array, bool isWaitCmd, bool isLinkCmd ) const {
  wxString cmd = cmds;
  while( !cmd.IsEmpty() ) {
    int idx = cmd.Find( _T( "\n" ) );
    wxString cmdpart = idx != -1 ? cmd.Left( idx ) : cmd;
    cmdpart.Trim( false );
    cmdpart.Trim( true );
    if( !cmdpart.IsEmpty() ) {
      if( isWaitCmd ) {
        array.Add( COMPILER_WAIT );
      }
      if( isLinkCmd ) {
        array.Add( COMPILER_WAIT_LINK );
      }
      array.Add( cmdpart );
    }
    if( idx == -1 ) {
      break;
    }
    cmd.Remove( 0, idx + 1 );
  }
}

static int MySortProjectFilesByWeight( ProjectFile** one, ProjectFile** two ) {
  int diff = ( *one )->weight - ( *two )->weight;
  diff = ( diff == 0 ? ( *one )->relativeFilename.CmpNoCase( ( *two )->relativeFilename ) : diff );
  return ( diff == 0 ? ( *one )->relativeFilename.Cmp( ( *two )->relativeFilename ) : diff );
}

MyFilesArray DirectCommands::GetProjectFilesSortedByWeight( ProjectBuildTarget* target, bool compile, bool link ) const {
  MyFilesArray files;
  for( FilesList::iterator it = m_pProject->GetFilesList().begin(); it != m_pProject->GetFilesList().end(); ++it ) {
    ProjectFile* pf = *it;
    if( compile && !pf->compile ) {
      continue;
    }
    if( link && !pf->link ) {
      continue;
    }
    if( target && ( pf->buildTargets.Index( target->GetTitle() ) == wxNOT_FOUND ) ) {
      continue;
    }
    files.Add( pf );
  }
  files.Sort( MySortProjectFilesByWeight );
  return files;
}

wxArrayString DirectCommands::CompileFile( ProjectBuildTarget* target, ProjectFile* pf, bool force ) const {
  wxArrayString ret;
  if( !pf || !pf->compile || pf->compilerVar.IsEmpty() ) {
    return ret;
  }
  if( target && pf->GetBuildTargets().Index( target->GetTitle() ) == wxNOT_FOUND ) {
    Manager::Get()->GetLogManager()->DebugLog( _T( "选择为编译项目文件的目标无效：文件不属于此目标." ) );
    return ret;
  }
  if( !force ) {
    DepsSearchStart( target );
    const pfDetails& pfd = pf->GetFileDetails( target );
    wxString err;
    if( !IsObjectOutdated( target, pfd, &err ) ) {
      if( !err.IsEmpty() ) {
        ret.Add( COMPILER_WARNING_LOG + err );
      }
      return ret;
    }
  }
  if( target ) {
    ret.Add( COMPILER_TARGET_CHANGE + target->GetTitle() );
  }
  AppendArray( GetCompileFileCommand( target, pf ), ret );
  return ret;
}

wxArrayString DirectCommands::GetCompileFileCommand( ProjectBuildTarget* target, ProjectFile* pf ) const {
  wxArrayString ret;
  wxArrayString ret_generated;
  if( !pf || !pf->compile ) {
    return ret;
  }
  if( pf->compilerVar.IsEmpty() ) {
    Manager::Get()->GetLogManager()->LogError( _T( "无法解析项目文件的编译器变量." ) );
    return ret;
  }
  Compiler* compiler = target ? CompilerFactory::GetCompiler( target->GetCompilerID() ) : m_pCompiler;
  if( !compiler ) {
    Manager::Get()->GetLogManager()->LogError( _T( "无法访问文件的编译器." ) );
    return ret;
  }
  const pfDetails& pfd = pf->GetFileDetails( target );
  wxString object = ( compiler->GetSwitches().UseFlatObjects ) ? pfd.object_file_flat : pfd.object_file;
  wxString object_dir  = ( compiler->GetSwitches().UseFlatObjects ) ? pfd.object_dir_flat_native : pfd.object_dir_native;
  if( !object_dir.IsEmpty() && !CreateDirRecursively( object_dir, 0755 ) ) {
    Manager::Get()->GetLogManager()->LogError( _T( "无法创建对象输出目录:\n" ) + object_dir );
  }
  const FileType ft = FileTypeOf( pf->relativeFilename );
  bool is_resource = ft == ftResource;
  bool is_header   = ft == ftHeader;
  wxString compiler_cmd;
  if( !is_header || compiler->GetSwitches().supportsPCH ) {
    const CompilerTool* tool = compiler->GetCompilerTool( is_resource ? ctCompileResourceCmd : ctCompileObjectCmd, pf->file.GetExt() );
    for( size_t i = 0; i < pf->generatedFiles.size(); ++i ) {
      AppendArray( GetCompileFileCommand( target, pf->generatedFiles[i] ), ret_generated );
    }
    pfCustomBuild& pcfb = pf->customBuild[compiler->GetID()];
    if( pcfb.useCustomBuildCommand ) {
      compiler_cmd = pcfb.buildCommand;
    } else if( tool ) {
      compiler_cmd = tool->command;
    } else
    { compiler_cmd = wxEmptyString; }
    wxString source_file;
    if( compiler->GetSwitches().UseFullSourcePaths ) {
      source_file = UnixFilename( pfd.source_file_absolute_native );
    } else
    { source_file = pfd.source_file; }
    if( is_resource && compiler->GetSwitches().UseFullSourcePaths ) {
      source_file = pf->file.GetShortPath();
    }
    QuoteStringIfNeeded( source_file );
    m_pGenerator->GenerateCommandLine( compiler_cmd, target, pf, source_file, object, pfd.object_file_flat, pfd.dep_file );
  }
  if( !is_header && compiler_cmd.IsEmpty() ) {
    ret.Add( COMPILER_SIMPLE_LOG + _T( "跳过项目中文件（没有编译器程序集）: " ) + pfd.source_file_native );
    return ret;
  }
  switch( compiler->GetSwitches().logging ) {
    case clogFull:
      ret.Add( COMPILER_SIMPLE_LOG + compiler_cmd );
      break;
    case clogSimple:
      if( is_header ) {
        ret.Add( COMPILER_SIMPLE_LOG + _T( "预编译头: " ) + pfd.source_file_native );
      } else
      { ret.Add( COMPILER_SIMPLE_LOG + _T( "编译: " ) + pfd.source_file_native ); }
      break;
    case clogNone:
    default:
      break;
  }
  AddCommandsToArray( compiler_cmd, ret );
  if( is_header ) {
    ret.Add( COMPILER_WAIT );
  }
  if( ret_generated.GetCount() ) {
    if( !is_header ) {
      ret.Add( COMPILER_WAIT );
    }
    AppendArray( ret_generated, ret );
  }
  if( ( ft == ftHeader ) && pf->compile ) {
    wxString object_abs = ( compiler->GetSwitches().UseFlatObjects ) ? pfd.object_file_flat_absolute_native : pfd.object_file_absolute_native;
    if( !wxRemoveFile( object_abs ) ) {
      Manager::Get()->GetLogManager()->DebugLog( _T( "无法删除旧的PCH文件:\n" ) + object_abs );
    }
  }
  return ret;
}


wxArrayString DirectCommands::GetCompileSingleFileCommand( const wxString& filename ) const {
  wxArrayString ret;
  FileType ft = FileTypeOf( filename );
  if( ft != ftSource ) {
    return ret;
  }
  wxFileName fname( filename );
  fname.SetExt( m_pCompiler->GetSwitches().objectExtension );
  wxString o_filename = fname.GetFullPath();
  wxString srcExt = fname.GetExt();
  fname.SetExt( FileFilters::EXECUTABLE_EXT );
  wxString exe_filename = fname.GetFullPath();
  wxString s_filename = filename;
  QuoteStringIfNeeded( s_filename );
  QuoteStringIfNeeded( o_filename );
  Compiler* compiler = CompilerFactory::GetDefaultCompiler();
  if( !compiler ) {
    return ret;
  }
  if( !m_pGenerator ) {
    m_Throw( _T( "命令生成器未通过ctor初始化!" ) );
  }
  wxString compilerCmd = compiler->GetCommand( ctCompileObjectCmd, srcExt );
  m_pGenerator->GenerateCommandLine( compilerCmd, 0, 0, s_filename, o_filename, o_filename, wxEmptyString );
  wxString linkerCmd = compiler->GetCommand( ctLinkConsoleExeCmd, fname.GetExt() );
  m_pGenerator->GenerateCommandLine( linkerCmd, 0, 0, wxEmptyString, o_filename, o_filename, wxEmptyString );
  if( !compilerCmd.IsEmpty() ) {
    switch( m_pCompiler->GetSwitches().logging ) {
      case clogFull:
        ret.Add( COMPILER_SIMPLE_LOG + compilerCmd );
        break;
      case clogSimple:
        ret.Add( COMPILER_SIMPLE_LOG + _T( "Compiling: " ) + filename );
        break;
      case clogNone:
      default:
        break;
    }
    AddCommandsToArray( compilerCmd, ret );
  } else {
    ret.Add( COMPILER_SIMPLE_LOG + _T( "跳过文件（没有编译器程序集）: " ) + filename );
  }
  if( !linkerCmd.IsEmpty() ) {
    switch( m_pCompiler->GetSwitches().logging ) {
      case clogFull:
        ret.Add( COMPILER_SIMPLE_LOG + linkerCmd );
        break;
      case clogSimple:
      case clogNone:
      default:
        ret.Add( COMPILER_SIMPLE_LOG + _T( "链接控制台可执行文件: " ) + exe_filename );
        break;
    }
    AddCommandsToArray( linkerCmd, ret, true );
  } else {
    ret.Add( COMPILER_SIMPLE_LOG + _T( "跳过链接（未设置链接程序）: " ) + exe_filename );
  }
  return ret;
}


wxArrayString DirectCommands::GetCleanSingleFileCommand( const wxString& filename ) const {
  wxArrayString ret;
  FileType ft = FileTypeOf( filename );
  if( ft != ftSource ) {
    return ret;
  }
  wxFileName fname( filename );
  fname.SetExt( m_pCompiler->GetSwitches().objectExtension );
  wxString o_filename = fname.GetFullPath();
  fname.SetExt( FileFilters::EXECUTABLE_EXT );
  wxString exe_filename = fname.GetFullPath();
  ret.Add( o_filename );
  ret.Add( exe_filename );
  return ret;
}

wxArrayString DirectCommands::GetCompileCommands( ProjectBuildTarget* target, bool force ) const {
  wxArrayString ret;
  if( target ) {
    ret = GetTargetCompileCommands( target, force );
  } else {
    for( int x = 0; x < m_pProject->GetBuildTargetsCount(); ++x ) {
      ProjectBuildTarget* bt = m_pProject->GetBuildTarget( x );
      if( bt->GetIncludeInTargetAll() ) {
        wxArrayString targetcompile = GetTargetCompileCommands( bt, force );
        AppendArray( targetcompile, ret );
      }
    }
  }
  return ret;
}

wxArrayString DirectCommands::GetTargetCompileCommands( ProjectBuildTarget* target, bool force ) const {
  wxArrayString ret;
  DepsSearchStart( target );
  size_t counter = ret.GetCount();
  MyFilesArray files = GetProjectFilesSortedByWeight( target, true, false );
  size_t fcount = files.GetCount();
  bool hasWeight = false;
  unsigned short int lastWeight = 0;
  for( unsigned int i = 0; i < fcount; ++i ) {
    ProjectFile* pf = files[i];
    if( pf->AutoGeneratedBy() ) {
      continue;
    }
    const pfDetails& pfd = pf->GetFileDetails( target );
    wxString err;
    if( force || IsObjectOutdated( target, pfd, &err ) ) {
      if( hasWeight && lastWeight != pf->weight && ( ret.IsEmpty() || ret.Last() != COMPILER_WAIT ) ) {
        ret.Add( COMPILER_WAIT );
      }
      wxArrayString filecmd = GetCompileFileCommand( target, pf );
      AppendArray( filecmd, ret );
      if( !hasWeight ) {
        hasWeight = true;
      }
      lastWeight = pf->weight;
    } else {
      if( !err.IsEmpty() ) {
        ret.Add( COMPILER_WARNING_LOG + err );
      }
    }
    if( m_doYield ) {
      Manager::Yield();
    }
  }
  wxArrayString link = GetLinkCommands( target, ret.GetCount() != counter );
  AppendArray( link, ret );
  return ret;
}

wxArrayString DirectCommands::GetPreBuildCommands( ProjectBuildTarget* target ) const {
  Compiler* compiler = target ? CompilerFactory::GetCompiler( target->GetCompilerID() ) : m_pCompiler;
  wxArrayString buildcmds = target ? target->GetCommandsBeforeBuild() : m_pProject->GetCommandsBeforeBuild();
  if( !buildcmds.IsEmpty() ) {
    wxString title = target ? target->GetTitle() : m_pProject->GetTitle();
    wxArrayString tmp;
    for( size_t i = 0; i < buildcmds.GetCount(); ++i ) {
      if( compiler ) {
        if( target ) {
          m_pGenerator->GenerateCommandLine( buildcmds[i], target, 0, wxEmptyString, wxEmptyString, wxEmptyString, wxEmptyString );
        } else
        { m_pGenerator->GenerateCommandLine( buildcmds[i], m_pProject->GetCurrentlyCompilingTarget(), 0, wxEmptyString, wxEmptyString, wxEmptyString, wxEmptyString ); }
      }
      tmp.Add( COMPILER_WAIT );
      tmp.Add( COMPILER_SIMPLE_LOG + buildcmds[i] );
      tmp.Add( buildcmds[i] );
    }
    buildcmds = tmp;
    if( target ) {
      buildcmds.Insert( COMPILER_SIMPLE_LOG + _T( "运行目标预生成步骤" ), 0 );
    } else
    { buildcmds.Insert( COMPILER_SIMPLE_LOG + _T( "运行项目预生成步骤" ), 0 ); }
    if( m_doYield ) {
      Manager::Yield();
    }
  }
  return buildcmds;
}

wxArrayString DirectCommands::GetPostBuildCommands( ProjectBuildTarget* target ) const {
  Compiler* compiler = target ? CompilerFactory::GetCompiler( target->GetCompilerID() ) : m_pCompiler;
  wxArrayString buildcmds = target ? target->GetCommandsAfterBuild() : m_pProject->GetCommandsAfterBuild();
  if( !buildcmds.IsEmpty() ) {
    wxString title = target ? target->GetTitle() : m_pProject->GetTitle();
    wxArrayString tmp;
    for( size_t i = 0; i < buildcmds.GetCount(); ++i ) {
      if( compiler ) {
        if( target ) {
          m_pGenerator->GenerateCommandLine( buildcmds[i], target, 0, wxEmptyString,
                                             wxEmptyString, wxEmptyString, wxEmptyString );
        } else {
          m_pGenerator->GenerateCommandLine( buildcmds[i], m_pProject->GetCurrentlyCompilingTarget(),
                                             0, wxEmptyString, wxEmptyString, wxEmptyString, wxEmptyString );
        }
      }
      tmp.Add( COMPILER_WAIT );
      tmp.Add( COMPILER_SIMPLE_LOG + buildcmds[i] );
      tmp.Add( buildcmds[i] );
    }
    buildcmds = tmp;
    if( target ) {
      buildcmds.Insert( COMPILER_SIMPLE_LOG + _T( "运行目标生成后步骤" ), 0 );
    } else
    { buildcmds.Insert( COMPILER_SIMPLE_LOG + _T( "运行项目后期生成步骤" ), 0 ); }
    if( m_doYield ) {
      Manager::Yield();
    }
  }
  return buildcmds;
}

wxArrayString DirectCommands::GetLinkCommands( ProjectBuildTarget* target, bool force ) const {
  wxArrayString ret;
  if( target ) {
    ret = GetTargetLinkCommands( target, force );
  } else {
    for( int x = 0; x < m_pProject->GetBuildTargetsCount(); ++x ) {
      ProjectBuildTarget* bt = m_pProject->GetBuildTarget( x );
      if( bt->GetIncludeInTargetAll() ) {
        wxArrayString targetlink = GetTargetLinkCommands( bt, force );
        AppendArray( targetlink, ret );
      }
    }
  }
  return ret;
}

wxArrayString DirectCommands::GetTargetLinkCommands( ProjectBuildTarget* target, bool force ) const {
  wxArrayString ret;
  wxString output = target->GetOutputFilename();
  Manager::Get()->GetMacrosManager()->ReplaceMacros( output, target );
  wxFileName out = UnixFilename( output );
  wxString linkfiles;
  wxString FlatLinkFiles;
  wxString resfiles;
  bool IsOpenWatcom = target->GetCompilerID().IsSameAs( _T( "ow" ) );
  time_t outputtime;
  depsTimeStamp( output.mb_str(), &outputtime );
  if( !outputtime ) {
    force = true;
  }
  wxArrayString fileMissing;
  if( AreExternalDepsOutdated( target, out.GetFullPath(), &fileMissing ) ) {
    force = true;
  }
  if( !fileMissing.IsEmpty() ) {
    wxString warn;
    warn.Printf( _T( "警告: 目标 '%s' --> 无法解析 %lu 外部依赖项:" ),
                 target->GetFullTitle().wx_str(), static_cast<unsigned long>( fileMissing.Count() ) );
    ret.Add( COMPILER_WARNING_LOG + warn );
    for( size_t i = 0; i < fileMissing.Count(); ++i ) {
      ret.Add( COMPILER_NOTE_LOG + wxString( _T( ' ' ), 8 ) + fileMissing[i] );
    }
  }
  Compiler* compiler = target ? CompilerFactory::GetCompiler( target->GetCompilerID() ) : m_pCompiler;
  wxString prependHack;
  if( target->GetTargetType() == ttStaticLib ) {
    wxString compilerCmd = compiler ? compiler->GetCommand( ctLinkStaticCmd ) : wxString( wxEmptyString );
    wxRegEx re( _T( "\\$([-+]+)link_objects" ) );
    if( re.Matches( compilerCmd ) ) {
      prependHack = re.GetMatch( compilerCmd, 1 );
    }
  }
  MyFilesArray files = GetProjectFilesSortedByWeight( target, false, true );
  if( files.GetCount() == 0 ) {
    if( target->GetTargetType() != ttCommandsOnly ) {
      ret.Add( COMPILER_SIMPLE_LOG + _T( "已跳过链接阶段（生成目标没有要链接的对象文件）" ) );
    }
    return ret;
  }
  if( IsOpenWatcom && target->GetTargetType() != ttStaticLib ) {
    linkfiles << _T( "file " );
  }
  bool subseq( false );
  for( unsigned int i = 0; i < files.GetCount(); ++i ) {
    ProjectFile* pf = files[i];
    wxString macro = _T( "$compiler" );
    m_pGenerator->GenerateCommandLine( macro, target, pf, wxEmptyString, wxEmptyString, wxEmptyString, wxEmptyString );
    if( macro.IsEmpty() ) {
      continue;
    }
    const pfDetails& pfd = pf->GetFileDetails( target );
    wxString Object = ( compiler->GetSwitches().UseFlatObjects ) ? pfd.object_file_flat
                      : pfd.object_file;
    if( FileTypeOf( pf->relativeFilename ) == ftResource ) {
      if( subseq ) {
        resfiles << _T( " " );
      }
      if( IsOpenWatcom ) {
        resfiles << _T( "option resource=" ) << Object;
      } else
      { resfiles << Object; }
    } else {
      if( IsOpenWatcom && target->GetTargetType() == ttStaticLib ) {
        if( subseq ) {
          linkfiles << _T( " " );
          FlatLinkFiles << _T( " " );
        }
        linkfiles << prependHack << Object;
        FlatLinkFiles << prependHack << pfd.object_file_flat;
      } else {
        if( subseq ) {
          linkfiles << compiler->GetSwitches().objectSeparator;
          FlatLinkFiles << compiler->GetSwitches().objectSeparator;
        }
        linkfiles << prependHack << Object;
        FlatLinkFiles << prependHack << pfd.object_file_flat;
      }
    }
    subseq = true;
    if( !force ) {
      time_t objtime;
      depsTimeStamp( pfd.object_file_native.mb_str(), &objtime );
      if( !objtime ) {
        force = true;
      }
      if( objtime > outputtime ) {
        force = true;
      }
    }
  }
  if( IsOpenWatcom ) {
    linkfiles.Trim();
  }
  if( !force ) {
    return ret;
  }
  out.MakeAbsolute( m_pProject->GetBasePath() );
  wxString dstname = out.GetPath( wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR );
  Manager::Get()->GetMacrosManager()->ReplaceMacros( dstname, target );
  if( !dstname.IsEmpty() && !CreateDirRecursively( dstname, 0755 ) ) {
    msging( _T( "无法创建输出目录 " ) + dstname );
  }
  wxString kind_of_output;
  CommandType ct = ctCount;
  switch( target->GetTargetType() ) {
    case ttConsoleOnly:
      ct = ctLinkConsoleExeCmd;
      kind_of_output = _T( "控制台程序" );
      break;
    case ttExecutable:
      ct = ctLinkExeCmd;
      kind_of_output = _T( "应用" );
      break;
    case ttDynamicLib:
      ct = ctLinkDynamicCmd;
      kind_of_output = _T( "动态库" );
      break;
    case ttStaticLib:
      ct = ctLinkStaticCmd;
      kind_of_output = _T( "静态库" );
      break;
    case ttNative:
      ct = ctLinkNativeCmd;
      kind_of_output = _T( "驱动" );
      break;
    case ttCommandsOnly:
      ret.Clear();
      AppendArray( GetPostBuildCommands( target ), ret );
      return ret;
      break;
    default:
      wxString ex;
      ex.Printf( _T( "遇到无效的 TargetType (值 = %d)" ), target->GetTargetType() );
      m_Throw( ex );
      break;
  }
  wxString compilerCmd = compiler->GetCommand( ct );
  m_pGenerator->GenerateCommandLine( compilerCmd, target, 0, _T( "" ), linkfiles, FlatLinkFiles, resfiles );
  if( !compilerCmd.IsEmpty() ) {
    switch( compiler->GetSwitches().logging ) {
      case clogFull:
        ret.Add( COMPILER_SIMPLE_LOG + compilerCmd );
        break;
      case clogSimple:
      case clogNone:
      default:
        ret.Add( COMPILER_SIMPLE_LOG + _T( "Linking " ) + kind_of_output + _T( ": " ) + output );
        break;
    }
    if( target && ret.GetCount() != 0 ) {
      ret.Add( COMPILER_TARGET_CHANGE + target->GetTitle() );
    }
    AddCommandsToArray( compilerCmd, ret, true, true );
  } else {
    ret.Add( COMPILER_SIMPLE_LOG + _T( "跳过项目文件链接（未设置链接程序）: " ) + output );
  }
  return ret;
}

wxArrayString DirectCommands::GetCleanCommands( ProjectBuildTarget* target, bool distclean ) const {
  wxArrayString ret;
  if( target ) {
    ret = GetTargetCleanCommands( target );
  } else {
    for( int x = 0; x < m_pProject->GetBuildTargetsCount(); ++x ) {
      ProjectBuildTarget* bt = m_pProject->GetBuildTarget( x );
      wxArrayString targetclear = GetTargetCleanCommands( bt, distclean );
      AppendArray( targetclear, ret );
    }
  }
  return ret;
}

wxArrayString DirectCommands::GetTargetCleanCommands( ProjectBuildTarget* target, bool distclean ) const {
  wxArrayString ret;
  MyFilesArray files = GetProjectFilesSortedByWeight( target, true, false );
  for( unsigned int i = 0; i < files.GetCount(); ++i ) {
    ProjectFile* pf = files[i];
    const pfDetails& pfd = pf->GetFileDetails( target );
    Compiler* compiler = target ? CompilerFactory::GetCompiler( target->GetCompilerID() ) : m_pCompiler;
    if( compiler ) {
      wxString ObjectAbs = ( compiler->GetSwitches().UseFlatObjects ) ? pfd.object_file_flat_absolute_native
                           : pfd.object_file_absolute_native;
      ret.Add( ObjectAbs );
      if( pf->AutoGeneratedBy() ) {
        ret.Add( pf->file.GetFullPath() );
      }
      if( distclean ) {
        ret.Add( pfd.dep_file_absolute_native );
      }
    }
  }
  wxString outputfilename = target->GetOutputFilename();
  if( target->GetTargetType() != ttCommandsOnly ) {
    Manager::Get()->GetMacrosManager()->ReplaceMacros( outputfilename, target );
    ret.Add( outputfilename );
  }
  if( target->GetTargetType() == ttDynamicLib ) {
    outputfilename = target->GetStaticLibFilename();
    Manager::Get()->GetMacrosManager()->ReplaceMacros( outputfilename, target );
    ret.Add( outputfilename );
  }
  return ret;
}


bool DirectCommands::AreExternalDepsOutdated( ProjectBuildTarget* target,
    const wxString& buildOutput,
    wxArrayString*  filesMissing ) const {
  Compiler* compiler = CompilerFactory::GetCompiler( target->GetCompilerID() );
  time_t timeOutput = 0;
  if( !buildOutput.IsEmpty() ) {
    wxString output = buildOutput;
    Manager::Get()->GetMacrosManager()->ReplaceMacros( output );
    depsTimeStamp( output.mb_str(), &timeOutput );
    if( timeOutput ) {
      wxArrayString libs = target->GetLinkLibs();
      const wxArrayString& prjLibs = target->GetParentProject()->GetLinkLibs();
      const wxArrayString& cmpLibs = compiler->GetLinkLibs();
      AppendArray( prjLibs, libs );
      AppendArray( cmpLibs, libs );
      const wxArrayString& prjLibDirs = target->GetParentProject()->GetLibDirs();
      const wxArrayString& cmpLibDirs = compiler->GetLibDirs();
      wxArrayString libDirs = target->GetLibDirs();
      AppendArray( prjLibDirs, libDirs );
      AppendArray( cmpLibDirs, libDirs );
      for( size_t i = 0; i < libs.GetCount(); ++i ) {
        wxString lib = libs[i];
        if( lib.Contains( _T( '/' ) ) || lib.Contains( _T( "\\" ) ) ) {
          Manager::Get()->GetMacrosManager()->ReplaceMacros( lib, target );
          lib = UnixFilename( lib );
          time_t timeExtDep;
          depsTimeStamp( lib.mb_str(), &timeExtDep );
          if( timeExtDep > timeOutput ) {
            Manager::Get()->GetLogManager()->DebugLog( F( _T( "Forcing re-link of '%s/%s' because '%s' is newer" ),
                target->GetParentProject()->GetTitle().wx_str(),
                target->GetTitle().wx_str(),
                lib.wx_str() ) );
            return true;
          }
          continue;
        }
        if( !lib.StartsWith( compiler->GetSwitches().libPrefix ) ) {
          lib = compiler->GetSwitches().libPrefix + lib;
        }
        if( !lib.EndsWith( _T( "." ) + compiler->GetSwitches().libExtension ) ) {
          lib += _T( "." ) + compiler->GetSwitches().libExtension;
        }
        for( size_t l = 0; l < libDirs.GetCount(); ++l ) {
          wxString dir = libDirs[l] + wxFILE_SEP_PATH + lib;
          Manager::Get()->GetMacrosManager()->ReplaceMacros( dir, target );
          dir = UnixFilename( dir );
          time_t timeExtDep;
          depsTimeStamp( dir.mb_str(), &timeExtDep );
          if( timeExtDep > timeOutput ) {
            Manager::Get()->GetLogManager()->DebugLog( F( _T( "Forcing re-link of '%s/%s' because '%s' is newer" ),
                target->GetParentProject()->GetTitle().wx_str(),
                target->GetTitle().wx_str(),
                dir.wx_str() ) );
            return true;
          }
        }
      }
    }
  }
  wxArrayString extDeps  = GetArrayFromString( target->GetExternalDeps(), _T( ";" ) );
  wxArrayString addFiles = GetArrayFromString( target->GetAdditionalOutputFiles(), _T( ";" ) );
  for( size_t i = 0; i < extDeps.GetCount(); ++i ) {
    if( extDeps[i].IsEmpty() ) {
      continue;
    }
    Manager::Get()->GetMacrosManager()->ReplaceMacros( extDeps[i] );
    time_t timeExtDep;
    depsTimeStamp( extDeps[i].mb_str(), &timeExtDep );
    if( !timeExtDep ) {
      if( filesMissing ) {
        filesMissing->Add( extDeps[i] );
      }
      continue;
    }
    for( size_t j = 0; j < addFiles.GetCount(); ++j ) {
      if( addFiles[j].IsEmpty() ) {
        continue;
      }
      Manager::Get()->GetMacrosManager()->ReplaceMacros( addFiles[j] );
      time_t timeAddFile;
      depsTimeStamp( addFiles[j].mb_str(), &timeAddFile );
      if( !timeAddFile ) {
        if( filesMissing ) {
          filesMissing->Add( addFiles[j] );
        }
        continue;
      }
      if( timeExtDep > timeAddFile ) {
        return true;
      }
    }
    if( buildOutput.IsEmpty() ) {
      continue;
    }
    if( !timeOutput ) {
      return true;
    }
    if( timeExtDep > timeOutput ) {
      return true;
    }
  }
  return false;
}

bool DirectCommands::IsObjectOutdated( ProjectBuildTarget* target, const pfDetails& pfd, wxString* errorStr ) const {
  time_t timeSrc;
  depsTimeStamp( pfd.source_file_absolute_native.mb_str(), &timeSrc );
  if( !timeSrc ) {
    if( errorStr ) {
      *errorStr = _T( "警告: 无法读取文件的时间戳: " ) + pfd.source_file_absolute_native;
    }
    if( wxFileExists( pfd.source_file_absolute_native ) ) {
      return true;
    }
    return false;
  }
  time_t timeObj;
  Compiler* compiler = target ? CompilerFactory::GetCompiler( target->GetCompilerID() ) : m_pCompiler;
  if( !compiler ) {
    return false;
  }
  wxString ObjectAbs = ( compiler->GetSwitches().UseFlatObjects ) ? pfd.object_file_flat_absolute_native
                       : pfd.object_file_absolute_native;
  depsTimeStamp( ObjectAbs.mb_str(), &timeObj );
  if( !timeObj ) {
    return true;
  }
  if( timeSrc > timeObj ) {
    return true;
  }
  if( Manager::Get()->GetConfigManager( _T( "compiler" ) )->ReadBool( _T( "/skip_include_deps" ), false ) ) {
    return false;
  }
  depsRef ref = depsScanForHeaders( pfd.source_file_absolute_native.mb_str() );
  if( ref ) {
    time_t timeNewest;
    ( void ) depsGetNewest( ref, &timeNewest );
    return ( timeNewest > timeObj );
  }
  return false;
}

void DirectCommands::DepsSearchStart( ProjectBuildTarget* target ) const {
  depsSearchStart();
  MacrosManager* mm = Manager::Get()->GetMacrosManager();
  wxArrayString incs = m_pGenerator->GetCompilerSearchDirs( target );
  for( unsigned int i = 0; i < incs.GetCount(); ++i ) {
    mm->ReplaceMacros( incs[i], target );
    depsAddSearchDir( incs[i].mb_str() );
  }
}
